const std = @import("std");
const csr = @import("riscv").csr;
const uart = @import("uart.zig");
const page = @import("page.zig");
const task = @import("task.zig");
const trap = @import("trap.zig");
const plic = @import("plic.zig");
const clint = @import("clint.zig");
const swtimer = @import("swtimer.zig");

const softintr = clint.software;
const timerintr = clint.timer;

pub const print = uart.print;
pub const allocator = page.fba.allocator();

pub fn panic(msg: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn {
    @branchHint(.cold);
    csr.clear(.mstatus, .{ .mie = true }); // disable interrupt

    print("\x1b[31m" ++ "KERNEL PANIC: {s}!\n" ++ "\x1b[m", .{msg});
    while (true) {}
}

const STACK_SIZE = 1024 * 16;
export var stack0: [STACK_SIZE]u8 align(16) = undefined;

const start_code = std.fmt.comptimePrint(
    \\  csrr    t0, mhartid
    \\  beqz    t0, hart_0
    \\
    \\park:
    \\  wfi
    \\  j       park
    \\
    \\hart_0:
    \\  # Setup stacks, the stack grows from bottom to top, so we put the
    \\  # stack pointer to the very end of the stack range.
    \\  la      sp, stack0 + {}     # set the initial stack pointer
    \\                              # to the end of the first stack space
    \\  j       kmain               # hart 0 jump to kmain function
, .{STACK_SIZE});

export fn _start() linksection(".os_start") callconv(.naked) noreturn {
    asm volatile (start_code);
}

export fn kmain() noreturn {
    uart.init();
    page.init();
    task.init();
    trap.init();
    plic.init();
    softintr.init();
    timerintr.init();
    swtimer.init();

    main() catch |e| @panic(@errorName(e));

    while (true) {}
}

fn main() !void {
    print("Hello, RVOS!\n", .{});

    page.info();
    print("\n", .{});

    // schedule() 的 switch_to 最后会执行 mret，第一次调用 schedule() 时
    // 不是在 trap handler 里，所以需要手动设置 mpp 和 mpie
    csr.set(.mstatus, .{ .mpie = true, .mpp = .machine });
    task.schedule();

    unreachable;
}
